#include "games/mazegame.h"

// TODO enum for -1,0 etc WALL etc
// TODO comment

MazeGame::MazeGame(QWidget *parent, int difflvl) :
    BaseGame(parent, difflvl)
{
    // Allow to grab keybouard (gaurantee focus)
    setFocus();
    setFocusPolicy(Qt::StrongFocus);

    // Load the map
    QString url = ":/data/" + QString("level%1.map").arg(difficulty_level);
    loadMap(url);

    // Startup variables
    teleport_x_locations = QList<int>();
    teleport_y_locations = QList<int>();
    number_of_keys = 0;

    if(difficulty_level <= 5) inverted_commands = false;
    else inverted_commands = true;
}

void MazeGame::loadMap(QString url)
{
    QFile * f = new QFile(url);
    if( !f->open(QIODevice::ReadOnly | QIODevice::Text)) return; // TODO handle

    QTextStream in(f);
    QStringList str_list = in.readLine().split("x");
    if( str_list.length() != 2 ) mapParseError(0);

    n_rows = str_list.at(0).toInt();
    n_cols = str_list.at(1).toInt();

    start_x = -1;
    teleport_x_arrival = -1;

    check_map = new QCheckBox**[n_rows];
    int_map = new int*[n_rows];
    for(int r = 0; r < n_rows; r++) {
        check_map[r] = new QCheckBox*[n_cols];
        int_map[r] = new int[n_cols];

        QString line = in.readLine();
        if(line.length() != n_cols ) mapParseError(1);

        for(int c = 0; c < n_cols; c++) {
            int value = symbolToInt(line.at(c));

            QCheckBox * p = new QCheckBox(this);
            check_map[r][c] = p;
            p->setEnabled(false);
            setValue(r, c, value);
            lay->addWidget(p, r, c);

            if ( value == 1 ) {
                y_pos = start_y = r;
                x_pos = start_x = c;
                p->setChecked(true);
            } else if ( value == 5 ) {
                teleport_y_locations << r;
                teleport_x_locations << c;
            } else if ( value == 6 ) {
                teleport_y_arrival = r;
                teleport_x_arrival = c;
            }
        }
    }

    // DEBUG MAP HANDLING
    if( start_x == -1 ) mapParseError(2);
    if( teleport_x_arrival == -1 ) {
        teleport_x_arrival = start_x;
        teleport_y_arrival = start_y;
    }

    f->close();
}

void MazeGame::mapParseError(int i = -1)
{
    switch(i) {
    case 0:
        qCritical() << "Error: invalid format.";
        break;
    case 1:
        qCritical() << "Error: dimensions do not agree.";
        break;
    case 2:
        qCritical() << "Error: no starting point.";
        break;
    case 3:
        qCritical() << "Error: invalid character.";
        break;
    default:
        qCritical() << "Error: unknown error.";
        break;
    }
}

int MazeGame::symbolToInt(const QChar c)
{
    if( c == ' ') return -1; // CLEAR
    else if( c == '#') return 0; // WALL
    else if( c == 'S') return 1; // START
    else if( c == 'E') return 2; // END
    else if( c == 'K') return 3; // KEY
    else if( c == 'D') return 4; // DOOR
    else if( c == 'T') return 5; // TELEPORT
    else if( c == 'A') return 6; // TELEPORT_ARRIVAL
    else if( c == 'X') return 7; // DEATH
    else if( c == 'G') return 8; // TREASURE
    else {
        mapParseError(3);
        return -1;
    }
}

QPalette MazeGame::getPalette(int i)
{
    if( i == 0 ) return QPalette(QColor(0,0,0)); // 0 for WALL (black)
    else if(i == 1) return QPalette(QColor(0,0,255)); // 1 for START (blue)
    else if(i == 2) return QPalette(QColor(0,255,0)); // 2 for END (green)
    else if(i == 3) return QPalette(QColor(255,255,0)); // 3 for KEY (yellow)
    else if(i == 4) return QPalette(QColor(100,100,100)); // 4 for DOOR (grey)
    else if(i == 5) return QPalette(QColor(0,255,255)); // 5 for TELEPORT (sky blue)
    else if(i == 6) return QPalette(QColor(255,0,255)); // 6 for TELEPORT_ARRIVAL (purple)
    else if(i == 7) return QPalette(QColor(255,0,0)); // 7 for DEATH (red)
    else if(i == 8) return QPalette(QColor(150,150,0)); // 8 for TREASURE (gold)
    else  return QPalette(QColor(255,255,255)); // white for ELSE
}

void MazeGame::setValue(int row, int col, int value)
{
    int_map[row][col] = value;
    if( value == -1 ) check_map[row][col]->setPalette(QPalette());
    else {
        check_map[row][col]->setPalette(getPalette(value));
    }
}

void MazeGame::moveTo(int row, int col)
{
    int value = int_map[row][col];
    if( value == 4 && number_of_keys > 0 ) { // AKA DOOR
        number_of_keys--;
        setValue(row, col, -1);
        check_map[row][col]->setChecked(true);
        check_map[y_pos][x_pos]->setChecked(false);
        y_pos = row;
        x_pos = col;
    } else if( value == 7 ) { // AKA DEATH
        emit pointsModification(-10 * difficulty_level);
        moveTo(start_y, start_x);
    } else if( value != 0 && value != 4 ) { // AKA NORMAL WITHOUT WALL
        check_map[y_pos][x_pos]->setChecked(false);
        check_map[row][col]->setChecked(true);
        y_pos = row;
        x_pos = col;
    }
    if( value == 8 ) { // AKA TREASURE
        emit pointsModification(10 * difficulty_level);
        setValue(row, col, -1);
    }
}

void MazeGame::moveUp()
{
    if(inverted_commands) moveTo(y_pos, x_pos - 1);
    else moveTo(y_pos - 1, x_pos);
}

void MazeGame::moveDown()
{
    if(inverted_commands) moveTo(y_pos, x_pos + 1);
    else moveTo(y_pos + 1, x_pos);
}

void MazeGame::moveLeft()
{
    if(inverted_commands) moveTo(y_pos - 1, x_pos);
    else moveTo(y_pos, x_pos - 1);
}

void MazeGame::moveRight()
{
    if(inverted_commands) moveTo(y_pos + 1, x_pos);
    else moveTo(y_pos, x_pos + 1);
}

void MazeGame::takeAction()
{
    int value = int_map[y_pos][x_pos];
    if( value == 3 ) { // AKA KEY
        number_of_keys++;
        setValue(y_pos, x_pos, -1);
    } else if ( value == 5 ) { // AKA TELEPORT
        moveTo(teleport_y_arrival, teleport_x_arrival);
    }
}

void MazeGame::keyPressEvent(QKeyEvent* key_event)
{
    if ( key_event->key() == Qt::Key_Up ) moveUp();
    else if( key_event->key() == Qt::Key_Down ) moveDown();
    else if( key_event->key() == Qt::Key_Left ) moveLeft();
    else if( key_event->key() == Qt::Key_Right ) moveRight();
    else if( key_event->key() == Qt::Key_Space ) takeAction();
    setFocus();
}

